package lol.clann.minecraft.springboot.plugin.bukkit.impl;

import lol.clann.minecraft.springboot.api.bukkit.utils.InventoryUtils;
import lol.clann.minecraft.springboot.api.bukkit.utils.ItemStackUtils;
import lol.clann.minecraft.springboot.api.context.SpringContext;
import lol.clann.minecraft.springboot.api.model.LazyField;
import lol.clann.minecraft.springboot.api.model.Slot;
import lombok.extern.slf4j.Slf4j;
import net.minecraft.server.v1_7_R4.IInventory;
import org.bukkit.Material;
import org.bukkit.craftbukkit.v1_7_R4.inventory.CraftInventory;
import org.bukkit.craftbukkit.v1_7_R4.inventory.CraftInventoryView;
import org.bukkit.craftbukkit.v1_7_R4.inventory.CraftItemStack;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.ItemStack;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.function.Predicate;

/**
 *
 * @author pyz
 * @date 2019/5/15 11:06 PM
 */
@Slf4j
@Service
public class InventoryUtilsImpl_all implements InventoryUtils {
    @Autowired
    private ItemStackUtils itemStackUtils;
    
    @Override
    public <T> T toNMS(Inventory inventory) {
        return (T) ((CraftInventory) inventory).getInventory();
    }

    /**
     * 标记一个背包需要保存
     * @param inventory
     */
    @Override
    public void markDirty(Inventory inventory) {
        ((IInventory) inventory).update();
    }

    @Override
    public <T> T toNMS(InventoryView inventoryView) {
        return (T) ((CraftInventoryView) inventoryView).getHandle();
    }

    @Override
    public boolean isNMSInventory(Object nmsTileEntity) {
        return nmsTileEntity instanceof IInventory;
    }

    @Override
    public int getSize(Object nmsInventory) {
        return ((IInventory) nmsInventory).getSize();
    }

    @Override
    public ItemStack getItem(Object nmsInventory, int index) {
        return itemStackUtils.toBukkit(((IInventory) nmsInventory).getItem(index));
    }

    @Override
    public void setItem(Object nmsInventory, int index, ItemStack itemStack) {
        if (itemStack instanceof CraftItemStack) {
            ((IInventory) nmsInventory).setItem(index, itemStackUtils.toNMS(itemStack));
        } else {
            ((IInventory) nmsInventory).setItem(index, itemStackUtils.asNMSCopy(itemStack));
        }
    }

    @Override
    public Map<Integer, ItemStack> getContent(Inventory inventory) {
        Map<Integer, ItemStack> map = new HashMap<>();
        for (Slot slot : allItems(inventory)) {
            map.put(slot.getIndex(), slot.getItem());
        }
        return map;
    }

    @Override
    public int capabilityFor(Inventory inventory, ItemStack itemStack) {
        if (itemStackUtils.isEmpty(itemStack)) {
            return 0;
        }
        int count = 0;
        int maxStackSize = Math.min(inventory.getMaxStackSize(), itemStack.getMaxStackSize());
        int size = inventory.getSize();
        for (int i = 0; i < size; i++) {
            ItemStack slot = inventory.getItem(i);
            if (slot == null || slot.getType() == Material.AIR) {
                count += maxStackSize;
            } else if (itemStackUtils.equalsIgnoreCount(slot, itemStack)) {
                count += Math.max(maxStackSize - slot.getAmount(), 0);
            }
        }
        return count;
    }

    @Override
    public List<Integer> emptySlots(Inventory inventory) {
        List<Integer> slots = new ArrayList<>();
        int size = inventory.getSize();
        for (int i = 0; i < size; i++) {
            if (itemStackUtils.isEmpty(inventory.getItem(i))) {
                slots.add(i);
            }
        }
        return slots;
    }

    /**
     * 对指定物品进行计数
     *
     * @param inventory
     * @param itemStack
     * @return
     */
    @Override
    public int count(Inventory inventory, ItemStack itemStack) {
        int count = 0;
        for (Slot slot : allItems(inventory, itemStack)) {
            count += slot.getItem().getAmount();
        }
        return count;
    }


    /**
     * 提取指定数量的物品
     * 数量不足会抛异常
     * -1表示全部提取
     *
     * @param inventory
     * @param itemStack
     * @return
     */
    @Override
    public Map<Integer, ItemStack> pull(Inventory inventory, ItemStack itemStack, int count) {
        Map<Integer, ItemStack> map = new HashMap<>();
        for (Slot slot : allItems(inventory, itemStack)) {
            int sub = Math.min(count, slot.getItem().getAmount());
            if (sub == 0) {
                return map;
            }
            ItemStack copy = slot.getItem().clone();
            copy.setAmount(sub);
            map.put(slot.getIndex(), copy);
            if (sub == slot.getItem().getAmount()) {
                slot.setItem(null);
            } else {
                slot.getItem().setAmount(slot.getItem().getAmount() - sub);
            }
            count -= sub;
        }
        return map;
    }

    @Override
    public int put(Inventory inventory, ItemStack itemStack, int count) {
        List<ItemStack> list = new ArrayList<>();
        Iterable<Slot> slots = allItems(inventory, i -> itemStackUtils.isEmpty(i) || (itemStackUtils.equalsIgnoreCount(i, itemStack) && i.getAmount() < i.getMaxStackSize() && i.getAmount() < inventory.getMaxStackSize()));
        for (Slot slot : slots) {
            if (count <= 0) {
                break;
            }
            int itemCount = slot.getItem() == null ? 0 : slot.getItem().getAmount();
            int maxStackSize = Math.min(itemStack.getMaxStackSize(), inventory.getMaxStackSize());
            int putCount = Math.min(count, maxStackSize - itemCount);
            count -= putCount;
            ItemStack putItem = itemStack.clone();
            putItem.setAmount(putCount);
            list.add(putItem);
        }
        inventory.addItem(list.toArray(new ItemStack[list.size()]));
        return count;
    }

    /**
     * 迭代所有格子
     * 包含空格子
     *
     * @param inventory
     * @return
     */
    @Override
    public Iterable<Slot> allSlots(Inventory inventory) {
        return () -> new InventoryIterator(inventory);
    }

    @Override
    public Iterable<Slot> allEmptySlots(Inventory inventory) {
        return () -> new InventoryIterator(inventory, i -> itemStackUtils.isEmpty(i));
    }

    /**
     * 迭代所有格子
     * 不包含空格子
     *
     * @param inventory
     * @return
     */
    @Override
    public Iterable<Slot> allItems(Inventory inventory) {
        return () -> new InventoryIterator(inventory, i -> !itemStackUtils.isEmpty(i));
    }

    /**
     * 迭代相同物品
     *
     * @param inventory
     * @param requireItemStack
     * @return
     */
    @Override
    public Iterable<Slot> allItems(Inventory inventory, ItemStack requireItemStack) {
        return () -> new InventoryIterator(inventory, requireItemStack);
    }

    /**
     * 迭代满足条件的物品
     *
     * @param inventory
     * @param filter
     * @return
     */
    @Override
    public Iterable<Slot> allItems(Inventory inventory, Predicate<ItemStack> filter) {
        return () -> new InventoryIterator(inventory, filter);
    }

    private static class InventoryIterator implements Iterator<Slot> {
        private static final LazyField<ItemStackUtils> itemStackUtils = LazyField.of(() -> SpringContext.getBean(ItemStackUtils.class));
        private int index = 0;
        private final int size;
        private final Inventory inventory;
        private final Predicate<ItemStack> filter;
        private Slot next;

        public InventoryIterator(Inventory inventory) {
            this(inventory, i -> true);
        }

        public InventoryIterator(Inventory inventory, ItemStack item) {
            this(inventory, i -> itemStackUtils.get().equalsIgnoreCount(i, item));
        }

        public InventoryIterator(Inventory inventory, Predicate<ItemStack> filter) {
            this.size = inventory.getSize();
            this.inventory = inventory;
            this.filter = filter;
            findNext();
        }

        @Override
        public boolean hasNext() {
            return next != null;
        }

        @Override
        public Slot next() {
            if (this.next == null) {
                throw new NoSuchElementException();
            }
            Slot next = this.next;
            findNext();
            return next;
        }

        private void findNext() {
            next = null;
            while (index < size) {
                ItemStack item = inventory.getItem(index);
                if (filter.test(item)) {
                    next = new Slot(index++, inventory);
                    return;
                }
                index++;
            }
        }
    }


}
